import os
import subprocess
import sys
from typing import List, Optional
import tkinter as tk
from tkinter import ttk, filedialog, messagebox


class TSMergerGUI:
    def __init__(self, root):
        self.root = root
        self.root.title("TS视频合并工具 - 合并为MP4")
        self.root.geometry("850x600")  # 加宽窗口，确保按钮不被遮挡
        self.root.resizable(True, True)

        # 全局变量
        self.folder_path: Optional[str] = None
        self.sorted_ts_files: List[str] = []
        self.output_filename = "merged_output.mp4"

        # 构建界面（优化布局）
        self._setup_ui()

    def _setup_ui(self):
        """重构界面布局，确保按钮可见"""
        # 1. 顶部控制区（使用网格布局，自适应窗口宽度）
        control_frame = ttk.Frame(self.root, padding="10")
        control_frame.pack(fill=tk.X, expand=False, anchor=tk.N)

        # 选择文件夹按钮
        self.select_folder_btn = ttk.Button(
            control_frame,
            text="选择TS视频文件夹",
            command=self.select_folder
        )
        self.select_folder_btn.grid(row=0, column=0, padx=5, pady=5, sticky=tk.W)

        # 文件夹路径显示（增加宽度，自动换行）
        self.folder_label = ttk.Label(
            control_frame,
            text="未选择文件夹",
            width=40,
            wraplength=300  # 限制宽度，超出换行
        )
        self.folder_label.grid(row=0, column=1, padx=5, pady=5, sticky=tk.W)

        # 输出文件名区域（单独一行，避免拥挤）
        ttk.Label(control_frame, text="输出文件名:").grid(row=1, column=0, padx=5, pady=5, sticky=tk.E)
        self.output_entry = ttk.Entry(control_frame, width=50)
        self.output_entry.insert(0, self.output_filename)
        self.output_entry.grid(row=1, column=1, padx=5, pady=5, sticky=tk.W + tk.E, columnspan=2)

        # 合并按钮（放大显示，确保可见）
        self.merge_btn = ttk.Button(
            control_frame,
            text="开始合并TS到MP4",
            command=self.start_merge,
            state=tk.DISABLED,
            style="Accent.TButton"  # 高亮样式
        )
        self.merge_btn.grid(row=0, column=2, padx=5, pady=5, sticky=tk.E)

        # 2. 中间文件列表区
        list_frame = ttk.Frame(self.root, padding="10")
        list_frame.pack(fill=tk.BOTH, expand=True, pady=(0, 10))

        # 列表标题
        title_frame = ttk.Frame(list_frame)
        title_frame.pack(fill=tk.X, expand=False, anchor=tk.W)
        ttk.Label(title_frame, text="已识别的TS文件（按数字顺序）:", font=("Arial", 11, "bold")).pack(side=tk.LEFT)
        self.file_count_label = ttk.Label(title_frame, text="(0个文件)")
        self.file_count_label.pack(side=tk.LEFT, padx=10)

        # 滚动条 + 列表框
        scrollbar = ttk.Scrollbar(list_frame)
        scrollbar.pack(side=tk.RIGHT, fill=tk.Y)

        self.file_listbox = tk.Listbox(
            list_frame,
            yscrollcommand=scrollbar.set,
            font=("Arial", 10),
            selectbackground="#e0e0e0",
            bg="white"
        )
        self.file_listbox.pack(fill=tk.BOTH, expand=True, padx=(0, 5))
        scrollbar.config(command=self.file_listbox.yview)

        # 3. 底部状态区
        status_frame = ttk.Frame(self.root, padding="10")
        status_frame.pack(fill=tk.X, expand=False, anchor=tk.S)

        self.status_label = ttk.Label(status_frame, text="就绪 - 请选择包含TS文件的文件夹", font=("Arial", 10))
        self.status_label.pack(anchor=tk.W, pady=5)

        # 进度条
        self.progress_var = tk.DoubleVar()
        self.progress_bar = ttk.Progressbar(
            status_frame,
            variable=self.progress_var,
            maximum=100,
            mode="determinate"
        )
        self.progress_bar.pack(fill=tk.X, padx=5, pady=5, expand=True)
        self.progress_bar.pack_forget()  # 初始隐藏

        # 增加样式配置（确保按钮高亮）
        self.root.style = ttk.Style()
        self.root.style.configure("Accent.TButton", font=("Arial", 10, "bold"), foreground="white",
                                  background="#2196F3")

    def get_sorted_ts_files(self, folder_path: str) -> List[str]:
        """查找并排序TS文件（核心逻辑不变）"""
        ts_files = []
        for filename in os.listdir(folder_path):
            if filename.endswith(".ts"):
                try:
                    # 提取文件名中的数字部分（支持纯数字或混合命名）
                    num_part = ''.join(filter(str.isdigit, filename))
                    if not num_part:
                        continue  # 跳过不含数字的TS文件
                    file_num = int(num_part)
                    ts_files.append((file_num, os.path.abspath(os.path.join(folder_path, filename))))
                except Exception:
                    continue

        # 按数字升序排序
        ts_files.sort(key=lambda x: x[0])
        return [path for _, path in ts_files]

    def select_folder(self):
        """选择文件夹并加载TS文件"""
        folder = filedialog.askdirectory(title="选择包含TS视频的文件夹")
        if not folder:
            return

        self.folder_path = folder
        # 更新文件夹路径显示（截取部分路径，避免过长）
        display_path = folder if len(folder) <= 40 else f"...{folder[-37:]}"
        self.folder_label.config(text=display_path)

        # 扫描TS文件
        self.status_label.config(text="正在扫描TS文件...")
        self.root.update_idletasks()

        try:
            self.sorted_ts_files = self.get_sorted_ts_files(folder)
            file_count = len(self.sorted_ts_files)
            self.file_count_label.config(text=f"({file_count}个文件)")

            if file_count == 0:
                messagebox.showwarning("警告", "该文件夹中未找到任何TS格式文件！")
                self.file_listbox.delete(0, tk.END)
                self.merge_btn.config(state=tk.DISABLED)
                self.status_label.config(text="就绪 - 未找到TS文件")
            else:
                # 更新文件列表
                self.file_listbox.delete(0, tk.END)
                for i, file_path in enumerate(self.sorted_ts_files, 1):
                    filename = os.path.basename(file_path)
                    self.file_listbox.insert(tk.END, f"{i:03d}. {filename}")

                # 启用合并按钮
                self.merge_btn.config(state=tk.NORMAL)
                self.status_label.config(text=f"就绪 - 已找到 {file_count} 个TS文件，可开始合并")
        except Exception as e:
            messagebox.showerror("错误", f"扫描文件失败：{str(e)}")
            self.status_label.config(text="扫描失败")

    def create_file_list(self, list_path: str) -> None:
        """创建TS文件列表（供ffmpeg使用）"""
        with open(list_path, "w", encoding="utf-8") as f:
            for file in self.sorted_ts_files:
                # 处理Windows路径反斜杠
                file_path = file.replace("\\", "/")
                f.write(f"file '{file_path}'\n")

    def start_merge(self):
        """开始合并TS文件"""
        if not self.folder_path or not self.sorted_ts_files:
            messagebox.showwarning("警告", "请先选择包含TS文件的文件夹！")
            return

        # 获取输出文件名
        output_name = self.output_entry.get().strip()
        if not output_name:
            messagebox.showwarning("警告", "输出文件名不能为空！")
            return

        # 确保输出格式是MP4
        if not output_name.lower().endswith(".mp4"):
            output_name += ".mp4"
            self.output_entry.delete(0, tk.END)
            self.output_entry.insert(0, output_name)

        output_path = os.path.join(self.folder_path, output_name)

        # 检查输出文件是否已存在
        if os.path.exists(output_path):
            if not messagebox.askyesno("确认覆盖", f"文件「{output_name}」已存在，是否覆盖？"):
                return

        # 准备合并（禁用控件，显示进度）
        self._set_controls_state(tk.DISABLED)
        self.status_label.config(text="正在准备合并...")
        self.progress_bar.pack(fill=tk.X, padx=5, pady=5, expand=True)
        self.progress_var.set(10)
        self.root.update_idletasks()

        try:
            # 1. 创建临时文件列表
            temp_list_path = os.path.join(self.folder_path, "temp_ts_list.txt")
            self.create_file_list(temp_list_path)
            self.progress_var.set(30)
            self.root.update_idletasks()

            # 2. 构建ffmpeg命令
            self.status_label.config(text="正在合并视频...（无损合并，速度很快）")
            self.root.update_idletasks()

            command = [
                "ffmpeg",
                "-f", "concat",
                "-safe", "0",
                "-i", temp_list_path,
                "-c", "copy",  # 无损复制，不重新编码
                "-bsf:a", "aac_adtstoasc",  # 修复音频兼容性
                "-y",  # 覆盖已存在文件（已提前确认）
                output_path
            ]

            # 3. 执行合并命令
            self.progress_var.set(50)
            result = subprocess.run(
                command,
                stdout=subprocess.PIPE,
                stderr=subprocess.STDOUT,
                text=True,
                encoding="utf-8",
                timeout=None
            )

            # 4. 检查结果
            if result.returncode == 0:
                self.progress_var.set(100)
                self.status_label.config(text="合并成功！")
                messagebox.showinfo(
                    "合并成功",
                    f"✅ 视频合并完成！\n\n输出文件：\n{output_name}\n\n文件位置：\n{self.folder_path}"
                )

                # 询问是否打开文件夹
                if messagebox.askyesno("打开文件夹", "是否立即打开输出文件所在文件夹？"):
                    self._open_folder(os.path.dirname(output_path))
            else:
                raise Exception(f"ffmpeg执行失败：\n{result.stdout[:500]}")  # 显示前500字符错误信息

        except Exception as e:
            self.status_label.config(text="合并失败！")
            messagebox.showerror("合并失败", f"❌ 合并过程中出现错误：\n{str(e)}")
            self.progress_var.set(0)
        finally:
            # 清理临时文件
            temp_list_path = os.path.join(self.folder_path, "temp_ts_list.txt")
            if os.path.exists(temp_list_path):
                try:
                    os.remove(temp_list_path)
                except:
                    pass

            # 恢复控件状态
            self._set_controls_state(tk.NORMAL)
            self.progress_bar.pack_forget()
            self.root.update_idletasks()

    def _set_controls_state(self, state: str):
        """设置所有控件的状态（启用/禁用）"""
        self.select_folder_btn.config(state=state)
        self.merge_btn.config(state=state)
        self.output_entry.config(state=state)

    def _open_folder(self, folder_path: str):
        """跨平台打开文件夹"""
        try:
            if sys.platform == "win32":
                os.startfile(folder_path)
            elif sys.platform == "darwin":
                subprocess.run(["open", folder_path])
            else:
                subprocess.run(["xdg-open", folder_path])
        except Exception as e:
            messagebox.showwarning("警告", f"无法打开文件夹：{str(e)}")


def main():
    # 检查ffmpeg是否安装
    try:
        subprocess.run(
            ["ffmpeg", "-version"],
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
            check=True,
            timeout=5
        )
    except (subprocess.CalledProcessError, FileNotFoundError, TimeoutError):
        messagebox.showerror(
            "错误 - 未找到ffmpeg",
            "❌ 无法找到ffmpeg工具！\n\n请先安装ffmpeg并配置环境变量：\n"
            "• Windows：下载后将bin目录添加到系统环境变量\n"
            "• Mac：打开终端执行 brew install ffmpeg\n"
            "• Linux：打开终端执行 sudo apt install ffmpeg"
        )
        sys.exit(1)

    # 启动GUI
    root = tk.Tk()
    app = TSMergerGUI(root)
    root.mainloop()


if __name__ == "__main__":
    main()